# --coding:utf-8--

# Copyright 2021 xpstem.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from enum import Enum, unique
from lelink.ev3type import *


@unique
class CommandType(Enum):
    DIRECT_COMMAND_REPLY = 0x00

    DIRECT_COMMAND_NO_REPLY = 0x80

    SYSTEM_COMMAND_REPLY = 0x01

    SYSTEM_COMMAND_NO_REPLY = 0x81


@unique
class ReplyType(Enum):
    UNKNOWN = 0xFF

    DIRECT_REPLY_OK = 0x02

    DIRECT_REPLY_ERROR = 0x04

    SYSTEM_REPLY_OK = 0x03

    SYSTEM_REPLY_ERROR = 0x05


@unique
class ReplyStatus(Enum):
    UNKNOWN = 0xFF
    SUCCESS = 0x00
    UNKNOWN_HANDLE = 0x01
    HANDLE_NOT_READY = 0x02
    CORRUPT_FILE = 0x03
    NO_HANDLES_AVAILABLE = 0x04
    NO_PERMISSION = 0x05
    ILLEGAL_PATH = 0x06
    FILE_EXITS = 0x07
    END_OF_FILE = 0x08
    SIZE_ERROR = 0x09
    UNKNOWN_ERROR = 0x0A
    ILLEGAL_FILENAME = 0x0B
    ILLEGAL_CONNECTION = 0x0C


class EV3CommandPackage:
    __slots__ = ("counter")

    def __init__(self):
        pass

    def package(self) -> bytearray:
        pass


class EV3ReplyPackage:
    __slots__ = ("reply_type",
                 "status",
                 "command_code",
                 "counter",
                 "rawdata")

    def __init__(self, counter: int, rawdata: bytearray):
        self.counter = counter
        self.rawdata = rawdata


class EV3Command:

    def __init__(self):
        pass

    def message(self) -> EV3CommandPackage:
        pass

    def response(self, reply_package: EV3ReplyPackage) -> None:
        pass

    def description(self) -> str:
        pass



class EV3DirectCommandPackage(EV3CommandPackage):
    __slots__ = ("_command_type",
                 "_package_list",
                 "counter")

    def __init__(self, command_type: CommandType, package_list: list):
        EV3CommandPackage.__init__(self)
        self._command_type = command_type
        self._package_list = package_list

    def package(self) -> bytearray:
        lvsize = 0;
        gvsize = 0;

        data_list = list()
        for package in self._package_list:
            data_list.append(package.bytes())
            lvsize += package.lv_size;
            gvsize += package.gv_size

        ba = bytearray()
        Data16(self.counter).write_to(ba)
        ba.append(self._command_type.value)

        header = (lvsize << 10) + gvsize
        ba.append(header & 0x00ff)
        ba.append((header >> 8) & 0x00ff)

        for data in data_list:
            ba.extend(data)
        return ba


class EV3DirectCommand(EV3Command):
    __slots__ = ("command_type",
                 "reply_type",
                 "_operation_list",
                 "_returnparam_list")

    def __init__(self,
                 operation_list: list,
                 command_type: CommandType = CommandType.DIRECT_COMMAND_REPLY):
        EV3Command.__init__(self)
        self.reply_type = None
        self.command_type = command_type
        self._operation_list = operation_list
        self._returnparam_list = list()
        for op in self._operation_list:
            param_list = op.returnparam_list
            if param_list is not None:
                self._returnparam_list.extend(param_list)

    def message(self) -> EV3CommandPackage:
        package_list = list()
        for op in self._operation_list:
            package_list.append(op.package)
        return EV3DirectCommandPackage(self.command_type, package_list)

    def response(self, reply_package: EV3ReplyPackage) -> None:
        ba = bytearray(reply_package.rawdata)
        # skip counter
        ba.pop(0)
        ba.pop(0)
        self.reply_type = ReplyType(ba.pop(0))
        reply_package.reply_type = self.reply_type
        if len(ba) == 0:
            return

        if self._returnparam_list is None:
            return

        gvdata = bytearray()
        while len(ba) > 0:
            gvdata.append(ba.pop(0))

        if self._returnparam_list is not None:
            for rp in self._returnparam_list:
                tmp = gvdata.copy()
                rp.parse(tmp, rp.index)

    def description(self) -> str:
        op_list = list()
        for op in self._operation_list:
            op_list.append(op.description())
        return "EV3DirectCommand({})".format(','.join(op_list))


class EV3SystemCommandPackage:
    __slots__ = ("_command_type",
                 "_command_code",
                 "_payload",
                 "counter")

    def __init__(self, command_type: CommandType, command_code: int, payload: bytearray):
        self._command_type = command_type
        self._command_code = command_code
        self._payload = payload

    def package(self) -> bytearray:
        ba = bytearray()
        Data16(self.counter).write_to(ba)
        ba.append(self._command_type.value)
        ba.append(self._command_code)
        ba.extend(self._payload)
        return ba


class EV3SystemCommand(EV3Command):
    __slots__ = ("command_type", "reply_type")

    def __init__(self,
                 command_type: CommandType = CommandType.SYSTEM_COMMAND_NO_REPLY):
        EV3Command.__init__(self)
        self.command_type = command_type

    def message(self) -> EV3CommandPackage:
        return EV3SystemCommandPackage(self.command_type, self.get_code(), self.get_payload())

    def response(self, reply_package: EV3ReplyPackage) -> None:
        ba = bytearray(reply_package.rawdata)
        # skip counter
        ba.pop(0)
        ba.pop(0)

    def description(self) -> str:
        return self.type()
